#include <mutex>
#include <stdio.h>
#include <chrono>
#include <unistd.h>
#include <fcntl.h>
#include <fstream>
#include <vector>
#include <filesystem>
#include "bin_parser.h"
#include "DeviceUpgradeUtil.h"
#include "Util/logc.h"
#include "md5_util.h"



using namespace DeviceUpgradeNameSpace;


static bool file_length(char *filename, uint32_t *length)
{
	FILE *fp = fopen(filename, "rb");
	if (NULL == fp) {
		aw_print("filename [%s] open fail",filename);
		*length = 0;
		return false;
	}

	fseek(fp, 0, SEEK_END);
	*length = ftell(fp);
	fseek(fp, 0, SEEK_SET);
	fclose(fp);
	return true;
}

static bool file_md5(char *filename, uint8_t *md5)
{
	uint32_t len;
	bool res = file_length(filename, &len);
	if (res == false || len <= 0) {
		aw_print("file [%s] length get fail", filename);
		return false;
	}

	char *buffer = awi_malloc(len);
	if (NULL == buffer) {
		aw_print("malloc len %d fail", len);
		return false;
	}

	FILE *fp = fopen(filename, "rb");
	if (NULL == fp) {
		aw_print("filename [%s] open fail",filename);
		return false;
	}

	int readed = fread(buffer, 1, len, fp);
	if (readed != len) {
		aw_print("read fail len = %d readed = %d", len, readed);
		fclose(fp);
		return false;
	}
	fclose(fp);

	MD5_CTX context;

	Dfu_MD5Init(&context);
	Dfu_MD5Update(&context, buffer, len);
	Dfu_MD5Final(md5, &context);

	printf("md5[");
	for (int i = 0; i < 16; ++i){
		printf("%2X", md5[i]);
	}
	printf("]\r\n");
	return true;
}



DeviceUpgradeUtil::DeviceUpgradeUtil(UpgradeType type){    
    switch(type){
        case A15SERIES:
            m_packetCount = 2;
            break;
        case A20SERIES:
            m_packetCount = 4;
            break;
        case VIDEOSERIES:
            m_packetCount = 1;
            break;
        default:
            m_packetCount = 0;
            break;
    }

    m_upgrade_type = type;
    m_upgrade_dfu = {};


    /**
     * 这里先思考一下每个升级函数做什么样的不同处理
     * 1、升级包的解压解析方式不一样，这主要指的是升级包的结构不一样，使用的解析工具类是一样的（bin_parser）
     * 1.1、这里引申出来的一个问题就是适应包结构所需要的结构体也是不一致的
     * 1.2、代码中recv函数，process函数的处理逻辑也是不同的
     * 1.3、这里需要确认的是使用的dfu类代码是否一致(同一套底层代码)
     * 2、包传输的交互逻辑上不一致
    */
}


//根据枚举类型的不同执行不一样的升级策略
bool DeviceUpgradeUtil::Upgrade(std::string devFilePath, const std::string& upgradeFilePath){
    m_upgrade_file_path = upgradeFilePath;
    m_device_file_path = devFilePath;
    warnf("-------------------------------------------------m_upgrade_file_path: %s , m_device_file_path: %s\n", m_upgrade_file_path.c_str(), m_device_file_path.c_str());
    //根据子设备的类型分发给不一样的升级函数
    switch(m_upgrade_type){
        case A15SERIES:
            return A15Upgrade();
        case A20SERIES:
            //增加升级函数
            //增加process解析和recv解析函数适应A20
            return A20Upgrade();
        case VIDEOSERIES:
            return VideoUpgrade();
        default:
            break;
    }
    return false;
}

bool DeviceUpgradeUtil::IsUpgrading(){
    return m_isUpgrading;
}


int DeviceUpgradeUtil::GetUpgradeProgress(){
    tracef("total_upgrade_size %lld, m_current_upgrade_size %lld\n", m_total_upgrade_size, m_current_upgrade_size);

    if(m_total_upgrade_size == 0 || m_current_upgrade_size == 0) return 0;

    return (m_current_upgrade_size*100)/m_total_upgrade_size;
}



void DeviceUpgradeUtil::upgradeSend(const uint8_t *data, int bytes){
    uint8_t buffer[BUFFER_SIZE];
    //aw_print("========== send length = %d", bytes);

    int pos = 0;
    while (pos < bytes) {
        int n = min(m_upgrade_dfu.step-1, bytes - pos);
        buffer[0] = 5;
        memset(buffer + 1, 0, m_upgrade_dfu.step-1);
        memcpy(buffer + 1, data + pos, n);

        /*
        printf("send [");
        for (int i = 0; i < 1024; ++i) {
            printf("%x ", buffer[i]);
        }
        printf("]\r\n");
        */

        int writed = write(m_upgrade_dfu.deice_fd, buffer, m_upgrade_dfu.step);
        //aw_print("writed = %d", writed);
        pos += n;
    }
}

 bool DeviceUpgradeUtil::setDigitalTotal(int total){
    m_digitaiTatal = total;
    return true;
 }


bool DeviceUpgradeUtil::VideoUpgrade(){
    m_total_upgrade_size = 0;
    m_current_upgrade_size = 0;

    std::string video_file_path  = "/extern/upgrade.bin";
    m_upgrade_dfu.dev_count = 1;
    m_upgrade_dfu.video_file = (char *)video_file_path.c_str();
    

    m_upgrade_dfu.dfu = awi_dfu_new();

    awi_dfu_set_send_notify(m_upgrade_dfu.dfu, this, [](void *obj, const uint8_t *data, int bytes){
        DeviceUpgradeUtil *own = (DeviceUpgradeUtil *)obj;
        if(own){
            own->upgradeSend(data, bytes);
        }
    });

    awi_dfu_set_recv_notify(m_upgrade_dfu.dfu, this, [](void *obj, uint16_t ctrl, 
        uint16_t dst, uint16_t src, uint16_t version, 
        const uint8_t *payload, int bytes){
            DeviceUpgradeUtil *own = (DeviceUpgradeUtil *)obj;
            if(own){
                own->deviceVideoUpgradeRecv(ctrl, dst, src, version, payload, bytes);
            }
    });

    m_upgrade_dfu.deice_fd = open(m_device_file_path.c_str(), O_RDWR, 0666);
    m_upgrade_dfu.step = 256;
    m_upgrade_dfu.slice_size = 235;

    m_isUpgrading = true;
    m_upgrade_dfu.downing = false;
    m_upgrade_dfu.error = false;
    m_upgrade_dfu.success = false;

    m_upgrade_dfu.fp = fopen(m_upgrade_dfu.video_file, "rb");
    if (NULL == m_upgrade_dfu.fp) {
        aw_print("open file [%s] fail", m_upgrade_dfu.video_file);
        m_upgrade_dfu.error = true;
        return ;
    }
    //这里需要下发bin的基本信息
    dfuVideoProcProcess();
    
    if (m_upgrade_dfu.fp) {
        fclose(m_upgrade_dfu.fp);
        m_upgrade_dfu.fp = NULL;
    }
    close(m_upgrade_dfu.deice_fd);
    awi_dfu_del(m_upgrade_dfu.dfu);
    m_upgrade_dfu = {};

    m_isUpgrading = false;
    
 
    if(std::filesystem::exists(video_file_path)) std::filesystem::remove(video_file_path);

    tracef(" send data size %lld\n", m_current_upgrade_size);
    if(m_current_upgrade_size != m_total_upgrade_size){
        return false;
    }
    return true;
}


bool DeviceUpgradeUtil::A20Upgrade(){
    m_total_upgrade_size = 0;
    m_current_upgrade_size = 0;

    std::string tinf_file_path  = "/extern/";
    std::string tips_file_path = "/extern/";
    std::string finf_file_path = "/extern/";
    std::string firm_file_path   = "/extern/";

    std::vector<std::string> file_list;
    auto bin_parser = std::make_shared<axdp::BinParser>();
    int ret = bin_parser->loadLocalFile(m_upgrade_file_path.c_str());
    ret = bin_parser->parseContent(file_list);
    if(file_list.size() != 5){
        tracef("parseContent  failed");
        return false;
    }

    for(int i = 1; i < bin_parser->getBlockNum(); i++){
        m_total_upgrade_size += bin_parser->blockLength(i);
    }

    // tracef("block length %d\n", m_total_upgrade_size);


    tinf_file_path  += file_list[4];
    tips_file_path += file_list[3];
    finf_file_path += file_list[2];
    firm_file_path  +=  file_list[1];
    
    std::ofstream algo_data_out(tinf_file_path, std::ios::out | std::ios::binary);
    algo_data_out.write((const char*)bin_parser->blockBuffer(4), bin_parser->blockLength(4));
    algo_data_out.close();

    std::ofstream rootfs_data_out(tips_file_path, std::ios::out | std::ios::binary);
    rootfs_data_out.write((const char*)bin_parser->blockBuffer(3), bin_parser->blockLength(3));
    rootfs_data_out.close();

    std::ofstream kernel_data_out(finf_file_path, std::ios::out | std::ios::binary);
    kernel_data_out.write((const char*)bin_parser->blockBuffer(2), bin_parser->blockLength(2));
    kernel_data_out.close();
   
    std::ofstream uboot_data_out(firm_file_path, std::ios::out | std::ios::binary);
    uboot_data_out.write((const char*)bin_parser->blockBuffer(1), bin_parser->blockLength(1));
    uboot_data_out.close();

    tracef(" tinf_file_path %s, tips_file_path %s, finf_file_path %s, firm_file_path %s", tinf_file_path.c_str(), tips_file_path.c_str(), finf_file_path.c_str(), firm_file_path.c_str());

    m_upgrade_dfu.dev_count = m_digitaiTatal;
    m_upgrade_dfu.tinf_file = (char *)tinf_file_path.c_str();
    m_upgrade_dfu.tips_file = (char *)tips_file_path.c_str();
    m_upgrade_dfu.finf_file = (char *)finf_file_path.c_str();
    m_upgrade_dfu.firm_file = (char *)firm_file_path.c_str();

    m_upgrade_dfu.dfu = awi_dfu_new();

    awi_dfu_set_send_notify(m_upgrade_dfu.dfu, this, [](void *obj, const uint8_t *data, int bytes){
        DeviceUpgradeUtil *own = (DeviceUpgradeUtil *)obj;
        if(own){
            own->upgradeSend(data, bytes);
        }
    });

    awi_dfu_set_recv_notify(m_upgrade_dfu.dfu, this, [](void *obj, uint16_t ctrl, 
        uint16_t dst, uint16_t src, uint16_t version, 
        const uint8_t *payload, int bytes){
            DeviceUpgradeUtil *own = (DeviceUpgradeUtil *)obj;
            if(own){
                own->deviceUpgradeRecv(ctrl, dst, src, version, payload, bytes);
            }
    });

    m_upgrade_dfu.deice_fd = open(m_device_file_path.c_str(), O_RDWR, 0666);
    m_upgrade_dfu.step = 256;
    m_upgrade_dfu.bin_index = 5;
    m_upgrade_dfu.downing = false;
    m_upgrade_dfu.error = false;
    m_upgrade_dfu.success = false;

    m_isUpgrading = true;
    dfuProcProcess();
    close(m_upgrade_dfu.deice_fd);
    awi_dfu_del(m_upgrade_dfu.dfu);
    m_upgrade_dfu = {};

    m_isUpgrading = false;
    
 
    if(std::filesystem::exists(tinf_file_path)) std::filesystem::remove(tinf_file_path);
    if(std::filesystem::exists(tips_file_path)) std::filesystem::remove(tips_file_path);
    if(std::filesystem::exists(finf_file_path)) std::filesystem::remove(finf_file_path);
    if(std::filesystem::exists(firm_file_path)) std::filesystem::remove(firm_file_path);

    tracef(" send data size %lld\n", m_current_upgrade_size);
    if(m_current_upgrade_size != m_total_upgrade_size){
        return false;
    }
    return true;
}



bool DeviceUpgradeUtil::A15Upgrade(){
    m_total_upgrade_size = 0;
    m_current_upgrade_size = 0;

    std::string ats_file_path = "/extern/";
    std::string rk_file_path = "/extern/";

    std::vector<std::string> file_list;
    auto bin_parser = std::make_shared<axdp::BinParser>();
    bin_parser->loadLocalFile(m_upgrade_file_path.c_str());

    bin_parser->parseContent(file_list);
    if(file_list.size() != 2){
        tracef("parseContent  failed");
        return false;
    }

    for(int i = 0; i < bin_parser->getBlockNum(); i++){
        m_total_upgrade_size += bin_parser->blockLength(i);
    }

    // tracef("block length %d\n", m_total_upgrade_size);


    ats_file_path +=  file_list[0];
    rk_file_path += file_list[1];
   
    std::ofstream rk_data_out(rk_file_path, std::ios::out | std::ios::binary);
    rk_data_out.write((const char*)bin_parser->blockBuffer(1), bin_parser->blockLength(1));
    rk_data_out.close();
   
    std::ofstream ats_data_out(ats_file_path, std::ios::out | std::ios::binary);
    ats_data_out.write((const char*)bin_parser->blockBuffer(0), bin_parser->blockLength(0));
    ats_data_out.close();

    tracef(" rk_file_path %s, ats_file_path%s", rk_file_path.c_str(), ats_file_path.c_str());

    m_upgrade_dfu.dev_count = 1;
    m_upgrade_dfu.ats_file = (char *)ats_file_path.c_str();
    m_upgrade_dfu.rk_file = (char *)rk_file_path.c_str();
    m_upgrade_dfu.dfu = awi_dfu_new();

    awi_dfu_set_send_notify(m_upgrade_dfu.dfu, this, [](void *obj, const uint8_t *data, int bytes){
        DeviceUpgradeUtil *own = (DeviceUpgradeUtil *)obj;
        if(own){
            own->upgradeSend(data, bytes);
        }
    });

    awi_dfu_set_recv_notify(m_upgrade_dfu.dfu, this, [](void *obj, uint16_t ctrl, 
        uint16_t dst, uint16_t src, uint16_t version, 
        const uint8_t *payload, int bytes){
            DeviceUpgradeUtil *own = (DeviceUpgradeUtil *)obj;
            if(own){
                own->deviceUpgradeRecv(ctrl, dst, src, version, payload, bytes);
            }
    });

    m_upgrade_dfu.deice_fd = open(m_device_file_path.c_str(), O_RDWR, 0666);
    m_upgrade_dfu.step = 256;
    m_upgrade_dfu.bin_index = 3;
    m_upgrade_dfu.downing = false;
    m_upgrade_dfu.error = false;
    m_upgrade_dfu.success = false;

    m_isUpgrading = true;
    dfuProcProcess();
    close(m_upgrade_dfu.deice_fd);
    awi_dfu_del(m_upgrade_dfu.dfu);
    m_upgrade_dfu = {};

    m_isUpgrading = false;
    if(std::filesystem::exists(ats_file_path)) std::filesystem::remove(ats_file_path);
    if(std::filesystem::exists(rk_file_path)) std::filesystem::remove(rk_file_path);

    tracef(" send data size %lld\n", m_current_upgrade_size);
    if(m_current_upgrade_size != m_total_upgrade_size){
        return false;
    }
    return true;
}

void DeviceUpgradeUtil::dfuProcProcess(){
    bool runing = true;

    DeviceUpgradeNameSpace::dfu_proc_t *dfu_proc = &m_upgrade_dfu;
    for (int i = 0; i < dfu_proc->dev_count; ++i) {
        dfu_proc->ready[i] = false;
    }
    awi_dfu_send(dfu_proc->dfu, AWI_DFU_PRO_UPGRADE_START_REQ_CODE, 0xFFFF, 0x01, NULL, 0);
    dfu_proc->downing = true;

    dfu_proc->dst = 0;
    for (int i = 1; i <= dfu_proc->dev_count; ++i) {
        dfu_proc->dst |= (1 << i);
    }
    aw_print("===== dfu proc dst = [%x]", dfu_proc->dst);

    while (runing) {
        aw_print("error = %d success = %d downing = %d", dfu_proc->error, dfu_proc->success, dfu_proc->downing);
        if (dfu_proc->error == true) {
            aw_print("error and exit");
            break;
        }
        else if (dfu_proc->success == true) {
            aw_print("successful");
            break;
        }
        else if (dfu_proc->downing == false) {
            switch (dfu_proc->bin_index) {
            case 4: {
				aw_print("======= start downding [%s] FB000004", dfu_proc->tinf_file);
				dfu_proc->flash_block = 0xFB000004;
				dfu_proc->bin_size = 0;
				bool res = file_length(dfu_proc->tinf_file, &dfu_proc->bin_size);
				if (res == false || dfu_proc->bin_size == 0) {
					aw_print("file [%s] length get fail", dfu_proc->tinf_file);
					dfu_proc->error = true;
					break;
				}

				dfu_proc->slice_size = BIN_STEP;
				dfu_proc->slices_total = dfu_proc->bin_size / dfu_proc->slice_size;
				if (dfu_proc->bin_size % dfu_proc->slice_size != 0) {
					dfu_proc->slices_total += 1;
				}
				dfu_proc->cur_slice_index = 0;

				uint8_t md5[16];
				res = file_md5(dfu_proc->tinf_file, md5);
				if (res == false) {
					aw_print("get file [%s] md5 fail", dfu_proc->tinf_file);
					dfu_proc->error = true;
					break;
				}

				dfu_proc->fp = fopen(dfu_proc->tinf_file, "rb");
				if (NULL == dfu_proc->fp) {
					aw_print("open file [%s] fail", dfu_proc->tinf_file);
					dfu_proc->error = true;
					break;
				}

				uint8_t up_buffer[sizeof(struct _dfu_bininfo)];
				struct _dfu_bininfo *up_info = (struct _dfu_bininfo*)up_buffer;
				up_info->flash_block = awi_htonl(dfu_proc->flash_block);
				up_info->slice_size = awi_htonl(dfu_proc->slice_size);
				up_info->slices_total = awi_htonl(dfu_proc->slices_total);
				up_info->bin_size = awi_htonl(dfu_proc->bin_size);
				memcpy(up_info->bin_md5, md5, 16);
				
				for (int i = 0; i < dfu_proc->dev_count; ++i) {
					dfu_proc->ready[i] = false;
				}
				awi_dfu_send(dfu_proc->dfu, AWI_DFU_PRO_UPGRADE_BIN_START_REQ_CODE, dfu_proc->dst, 0x01, up_buffer, sizeof(struct _dfu_bininfo));
				dfu_proc->downing = true;
				break;
			}
			case 3: {
				aw_print("======= start downding [%s] FB000003", dfu_proc->tips_file);
				dfu_proc->flash_block = 0xFB000003;
				dfu_proc->bin_size = 0;
				bool res = file_length(dfu_proc->tips_file, &dfu_proc->bin_size);
				if (res == false || dfu_proc->bin_size == 0) {
					aw_print("file [%s] length get fail", dfu_proc->tips_file);
					dfu_proc->error = true;
					break;
				}

				dfu_proc->slice_size = BIN_STEP;
				dfu_proc->slices_total = dfu_proc->bin_size / dfu_proc->slice_size;
				if (dfu_proc->bin_size % dfu_proc->slice_size != 0) {
					dfu_proc->slices_total += 1;
				}
				dfu_proc->cur_slice_index = 0;

				uint8_t md5[16];
				res = file_md5(dfu_proc->tips_file, md5);
				if (res == false) {
					aw_print("get file [%s] md5 fail", dfu_proc->tips_file);
					dfu_proc->error = true;
					break;
				}

				dfu_proc->fp = fopen(dfu_proc->tips_file, "rb");
				if (NULL == dfu_proc->fp) {
					aw_print("open file [%s] fail", dfu_proc->tips_file);
					dfu_proc->error = true;
					break;
				}

				uint8_t up_buffer[sizeof(struct _dfu_bininfo)];
				struct _dfu_bininfo *up_info = (struct _dfu_bininfo*)up_buffer;
				up_info->flash_block = awi_htonl(dfu_proc->flash_block);
				up_info->slice_size = awi_htonl(dfu_proc->slice_size);
				up_info->slices_total = awi_htonl(dfu_proc->slices_total);
				up_info->bin_size = awi_htonl(dfu_proc->bin_size);
				memcpy(up_info->bin_md5, md5, 16);

				for (int i = 0; i < dfu_proc->dev_count; ++i) {
					dfu_proc->ready[i] = false;
				}
				awi_dfu_send(dfu_proc->dfu, AWI_DFU_PRO_UPGRADE_BIN_START_REQ_CODE, dfu_proc->dst, 0x01, up_buffer, sizeof(struct _dfu_bininfo));
				dfu_proc->downing = true;
				break;
			}
            case 2:{
                //根据升级的类型下发不一样的升级包文件开始传输
                if(m_upgrade_type == A15SERIES){
                    aw_print("======= start downding [%s] FB000001", dfu_proc->ats_file);
                    dfu_proc->flash_block = 0xFB000001;
                    dfu_proc->bin_size = 0;
                    bool res = file_length(dfu_proc->ats_file, &dfu_proc->bin_size);
                    if (res == false || dfu_proc->bin_size == 0) {
                        aw_print("file [%s] length get fail", dfu_proc->ats_file);
                        dfu_proc->error = true;
                        break;
                    }

                    dfu_proc->slice_size = BIN_STEP;
                    dfu_proc->slices_total = dfu_proc->bin_size / dfu_proc->slice_size;
                    if (dfu_proc->bin_size % dfu_proc->slice_size != 0) {
                        dfu_proc->slices_total += 1;
                    }
                    dfu_proc->cur_slice_index = 0;

                    uint8_t md5[16];
                    res = file_md5(dfu_proc->ats_file, md5);
                    if (res == false) {
                        aw_print("get file [%s] md5 fail", dfu_proc->ats_file);
                        dfu_proc->error = true;
                        break;
                    }

                    dfu_proc->fp = fopen(dfu_proc->ats_file, "rb");
                    if (NULL == dfu_proc->fp) {
                        aw_print("open file [%s] fail", dfu_proc->ats_file);
                        dfu_proc->error = true;
                        break;
                    }

                    uint8_t up_buffer[sizeof(struct _dfu_bininfo)];
                    struct _dfu_bininfo *up_info = (struct _dfu_bininfo*)up_buffer;
                    up_info->flash_block = awi_htonl(dfu_proc->flash_block);
                    up_info->slice_size = awi_htonl(dfu_proc->slice_size);
                    up_info->slices_total = awi_htonl(dfu_proc->slices_total);
                    up_info->bin_size = awi_htonl(dfu_proc->bin_size);
                    memcpy(up_info->bin_md5, md5, 16);

                    for (int i = 0; i < dfu_proc->dev_count; ++i) {
                        dfu_proc->ready[i] = false;
                    }
                    awi_dfu_send(dfu_proc->dfu, AWI_DFU_PRO_UPGRADE_BIN_START_REQ_CODE, dfu_proc->dst, 0x01, up_buffer, sizeof(struct _dfu_bininfo));
                    dfu_proc->downing = true;
                    break;
                }else if(m_upgrade_type == A20SERIES){
                    aw_print("======= start downding [%s] FB000002", dfu_proc->finf_file);
                    dfu_proc->flash_block = 0xFB000002;
                    dfu_proc->bin_size = 0;
                    bool res = file_length(dfu_proc->finf_file, &dfu_proc->bin_size);
                    if (res == false || dfu_proc->bin_size == 0) {
                        aw_print("file [%s] length get fail", dfu_proc->finf_file);
                        dfu_proc->error = true;
                        break;
                    }

                    dfu_proc->slice_size = BIN_STEP;
                    dfu_proc->slices_total = dfu_proc->bin_size / dfu_proc->slice_size;
                    if (dfu_proc->bin_size % dfu_proc->slice_size != 0) {
                        dfu_proc->slices_total += 1;
                    }
                    dfu_proc->cur_slice_index = 0;

                    uint8_t md5[16];
                    res = file_md5(dfu_proc->finf_file, md5);
                    if (res == false) {
                        aw_print("get file [%s] md5 fail", dfu_proc->finf_file);
                        dfu_proc->error = true;
                        break;
                    }

                    dfu_proc->fp = fopen(dfu_proc->finf_file, "rb");
                    if (NULL == dfu_proc->fp) {
                        aw_print("open file [%s] fail", dfu_proc->finf_file);
                        dfu_proc->error = true;
                        break;
                    }

                    uint8_t up_buffer[sizeof(struct _dfu_bininfo)];
                    struct _dfu_bininfo *up_info = (struct _dfu_bininfo*)up_buffer;
                    up_info->flash_block = awi_htonl(dfu_proc->flash_block);
                    up_info->slice_size = awi_htonl(dfu_proc->slice_size);
                    up_info->slices_total = awi_htonl(dfu_proc->slices_total);
                    up_info->bin_size = awi_htonl(dfu_proc->bin_size);
                    memcpy(up_info->bin_md5, md5, 16);

                    for (int i = 0; i < dfu_proc->dev_count; ++i) {
                        dfu_proc->ready[i] = false;
                    }
                    awi_dfu_send(dfu_proc->dfu, AWI_DFU_PRO_UPGRADE_BIN_START_REQ_CODE, dfu_proc->dst, 0x01, up_buffer, sizeof(struct _dfu_bininfo));
                    dfu_proc->downing = true;
                    break;
                }
            }
            case 1:{
                
                //根据升级的类型下发不一样的升级包文件开始传输
                if(m_upgrade_type == A15SERIES){
                    aw_print("======= start downding [%s] FB000002", dfu_proc->rk_file);
                    dfu_proc->flash_block = 0xFB000002;
                    dfu_proc->bin_size = 0;
                    bool res = file_length(dfu_proc->rk_file, &dfu_proc->bin_size);
                    if (res == false || dfu_proc->bin_size == 0) {
                        aw_print("file [%s] length get fail", dfu_proc->rk_file);
                        dfu_proc->error = true;
                        break;
                    }

                    dfu_proc->slice_size = BIN_STEP;
                    dfu_proc->slices_total = dfu_proc->bin_size / dfu_proc->slice_size;
                    if (dfu_proc->bin_size % dfu_proc->slice_size != 0) {
                        dfu_proc->slices_total += 1;
                    }
                    dfu_proc->cur_slice_index = 0;

                    uint8_t md5[16];
                    res = file_md5(dfu_proc->rk_file, md5);
                    if (res == false) {
                        aw_print("get file [%s] md5 fail", dfu_proc->rk_file);
                        dfu_proc->error = true;
                        break;
                    }

                    dfu_proc->fp = fopen(dfu_proc->rk_file, "rb");
                    if (NULL == dfu_proc->fp) {
                        aw_print("open file [%s] fail", dfu_proc->rk_file);
                        dfu_proc->error = true;
                        break;
                    }

                    uint8_t up_buffer[sizeof(struct _dfu_bininfo)];
                    struct _dfu_bininfo *up_info = (struct _dfu_bininfo*)up_buffer;
                    up_info->flash_block = awi_htonl(dfu_proc->flash_block);
                    up_info->slice_size = awi_htonl(dfu_proc->slice_size);
                    up_info->slices_total = awi_htonl(dfu_proc->slices_total);
                    up_info->bin_size = awi_htonl(dfu_proc->bin_size);
                    memcpy(up_info->bin_md5, md5, 16);

                    for (int i = 0; i < dfu_proc->dev_count; ++i) {
                        dfu_proc->ready[i] = false;
                    }
                    awi_dfu_send(dfu_proc->dfu, AWI_DFU_PRO_UPGRADE_BIN_START_REQ_CODE, dfu_proc->dst, 0x01, up_buffer, sizeof(struct _dfu_bininfo));
                    dfu_proc->downing = true;
                    break;
                }else if(m_upgrade_type == A20SERIES){
                    aw_print("======= start downding [%s] FB000001", dfu_proc->firm_file);
                    dfu_proc->flash_block = 0xFB000001;
                    dfu_proc->bin_size = 0;
                    bool res = file_length(dfu_proc->firm_file, &dfu_proc->bin_size);
                    if (res == false || dfu_proc->bin_size == 0) {
                        aw_print("file [%s] length get fail", dfu_proc->firm_file);
                        dfu_proc->error = true;
                        break;
                    }

                    dfu_proc->slice_size = BIN_STEP;
                    dfu_proc->slices_total = dfu_proc->bin_size / dfu_proc->slice_size;
                    if (dfu_proc->bin_size % dfu_proc->slice_size != 0) {
                        dfu_proc->slices_total += 1;
                    }
                    dfu_proc->cur_slice_index = 0;

                    uint8_t md5[16];
                    res = file_md5(dfu_proc->firm_file, md5);
                    if (res == false) {
                        aw_print("get file [%s] md5 fail", dfu_proc->firm_file);
                        dfu_proc->error = true;
                        break;
                    }

                    dfu_proc->fp = fopen(dfu_proc->firm_file, "rb");
                    if (NULL == dfu_proc->fp) {
                        aw_print("open file [%s] fail", dfu_proc->firm_file);
                        dfu_proc->error = true;
                        break;
                    }

                    uint8_t up_buffer[sizeof(struct _dfu_bininfo)];
                    struct _dfu_bininfo *up_info = (struct _dfu_bininfo*)up_buffer;
                    up_info->flash_block = awi_htonl(dfu_proc->flash_block);
                    up_info->slice_size = awi_htonl(dfu_proc->slice_size);
                    up_info->slices_total = awi_htonl(dfu_proc->slices_total);
                    up_info->bin_size = awi_htonl(dfu_proc->bin_size);
                    memcpy(up_info->bin_md5, md5, 16);

                    for (int i = 0; i < dfu_proc->dev_count; ++i) {
                        dfu_proc->ready[i] = false;
                    }
                    awi_dfu_send(dfu_proc->dfu, AWI_DFU_PRO_UPGRADE_BIN_START_REQ_CODE, dfu_proc->dst, 0x01, up_buffer, sizeof(struct _dfu_bininfo));
                    dfu_proc->downing = true;
                    break;
                }else if(m_upgrade_type == VIDEOSERIES){
                    aw_print("======= start downding [%s] FB000001", dfu_proc->video_file);
                    dfu_proc->flash_block = 0xFB000001;
                    dfu_proc->bin_size = 0;
                    bool res = file_length(dfu_proc->video_file, &dfu_proc->bin_size);
                    if (res == false || dfu_proc->bin_size == 0) {
                        aw_print("file [%s] length get fail", dfu_proc->video_file);
                        dfu_proc->error = true;
                        break;
                    }

                    dfu_proc->slice_size = dfu_proc->bin_size;
                    dfu_proc->slices_total = dfu_proc->bin_size / dfu_proc->slice_size;
                    if (dfu_proc->bin_size % dfu_proc->slice_size != 0) {
                        dfu_proc->slices_total += 1;
                    }
                    dfu_proc->cur_slice_index = 0;

                    uint8_t md5[16];
                    res = file_md5(dfu_proc->video_file, md5);
                    if (res == false) {
                        aw_print("get file [%s] md5 fail", dfu_proc->video_file);
                        dfu_proc->error = true;
                        break;
                    }

                    dfu_proc->fp = fopen(dfu_proc->video_file, "rb");
                    if (NULL == dfu_proc->fp) {
                        aw_print("open file [%s] fail", dfu_proc->video_file);
                        dfu_proc->error = true;
                        break;
                    }

                    uint8_t up_buffer[sizeof(struct _dfu_bininfo)];
                    struct _dfu_bininfo *up_info = (struct _dfu_bininfo*)up_buffer;
                    up_info->flash_block = awi_htonl(dfu_proc->flash_block);
                    up_info->slice_size = awi_htonl(dfu_proc->slice_size);
                    up_info->slices_total = awi_htonl(dfu_proc->slices_total);
                    up_info->bin_size = awi_htonl(dfu_proc->bin_size);
                    memcpy(up_info->bin_md5, md5, 16);

                    for (int i = 0; i < dfu_proc->dev_count; ++i) {
                        dfu_proc->ready[i] = false;
                    }
                    awi_dfu_send(dfu_proc->dfu, AWI_DFU_PRO_UPGRADE_BIN_START_REQ_CODE, dfu_proc->dst, 0x01, up_buffer, sizeof(struct _dfu_bininfo));
                    dfu_proc->downing = true;
                    break;
                }
            }
            case 0: {
                aw_print("dfu upgrade stop");
                uint32_t success = 0;
                for (int i = 0; i < dfu_proc->dev_count; ++i) {
                    dfu_proc->ready[i] = false;
                }
                awi_dfu_send(dfu_proc->dfu, AWI_DFU_PRO_UPGRADE_STOP_REQ_CODE, 0xFFFF, 0x01, (const uint8_t*)&success, 4);
                dfu_proc->downing = true;
                break;
            }
            }
        }
        else {
            uint8_t buffer[BUFFER_SIZE];

            int len = read(dfu_proc->deice_fd, buffer, dfu_proc->step);

            aw_print("read len = %d", len);
            if (len != dfu_proc->step) {
                aw_print("hid read timeout");
                dfu_proc->error = true;
            }
            else {
                /*
                printf("recv[");
                for (int i = 0; i < len; ++i) {
                    printf("%x ", buffer[i]);
                }
                printf("]\r\n");
                */
                awi_dfu_recv(dfu_proc->dfu, buffer + 1, len-1);
            }
        }
    }
    return ;
}


void DeviceUpgradeUtil::deviceUpgradeRecv(uint16_t ctrl, uint16_t dst, uint16_t src, uint16_t version, const uint8_t *payload, int bytes){
    DeviceUpgradeNameSpace::dfu_proc_t* dfu_proc = &m_upgrade_dfu;
    aw_print("bin index = %d", dfu_proc->bin_index);
    

    if(dfu_proc->bin_index == m_packetCount + 1){
        /* Receive the overall upgrade start protocol */
        if (ctrl == AWI_DFU_PRO_UPGRADE_START_REP_CODE) {
            uint32_t *pv = (uint32_t*)payload;
            if (bytes == 4 && awi_ntohl(pv[0]) == 0) {
                bool pass = true;
                for (int i = 1; i <= dfu_proc->dev_count; ++i) {
                    aw_print("%d : ready = %d", i, dfu_proc->ready[i - 1]);
                    if ((src & (1 << i))) {
                        dfu_proc->ready[i - 1] = true;
                    }
                    else if (dfu_proc->ready[i - 1] == false) {
                        pass = false;
                    }
                }
                if (pass == true) {
                    dfu_proc->downing = false;
                    --dfu_proc->bin_index;
                }
            }
            else {
                dfu_proc->error = true;
            }
        }
    }else if(dfu_proc->bin_index > 0){
        /* Process for sending a single upgrade package */
        aw_print("slice size = %d index = %d total = %d bin size = %d", dfu_proc->slice_size, dfu_proc->cur_slice_index, dfu_proc->slices_total, dfu_proc->bin_size);
        if (dfu_proc->cur_slice_index == 0) {
            if (ctrl == AWI_DFU_PRO_UPGRADE_BIN_START_REP_CODE) {
                uint32_t *pv = (uint32_t*)payload;
                if (bytes == 4 && awi_ntohl(pv[0]) == 0) {
                    bool pass = true;
                    for (int i = 1; i <= dfu_proc->dev_count; ++i) {
                        aw_print("%d : ready = %d", i, dfu_proc->ready[i - 1]);
                        if ((src & (1 << i))) {
                            dfu_proc->ready[i - 1] = true;
                        }
                        else if (dfu_proc->ready[i - 1] == false) {
                            pass = false;
                        }
                    }
                    if (pass == true) {
                        uint8_t buffer[BIN_STEP + 4];
                        ++dfu_proc->cur_slice_index;
                        
                        uint32_t *pindex = (uint32_t*)buffer;
                        pindex[0] = awi_htonl(dfu_proc->cur_slice_index);
                        int readed = fread(buffer + 4, 1, BIN_STEP, dfu_proc->fp);
                        m_current_upgrade_size += readed;
                        aw_print("index = %d readed length = %d", dfu_proc->cur_slice_index, readed);
                        if (readed <= 0) {
                            aw_print("readed fail");
                            dfu_proc->error = true;
                            return ;
                        }
                        if (readed != BIN_STEP && dfu_proc->cur_slice_index != dfu_proc->slices_total) {
                            aw_print("slice index [%d / %d] length = %d error", dfu_proc->cur_slice_index, dfu_proc->slices_total, readed);
                            dfu_proc->error = true;
                            return ;
                        }
                        for (int i = 0; i < dfu_proc->dev_count; ++i) {
                            dfu_proc->ready[i] = false;
                        }
                        awi_dfu_send(dfu_proc->dfu, AWI_DFU_PRO_UPGRADE_BIN_REQ_CODE, dfu_proc->dst, 0x01, buffer, readed + 4);
                    }
                }
                else {
                    aw_print("error");
                    dfu_proc->error = true;
                }
            }
        }
        else if (dfu_proc->cur_slice_index == dfu_proc->slices_total) {
            if (ctrl == AWI_DFU_PRO_UPGRADE_BIN_REP_CODE) {
                uint32_t *pv = (uint32_t*)payload;
                if (bytes == 4 && awi_htonl(pv[0]) == 0xFFFFFFFF) {
                    bool pass = true;
                    for (int i = 1; i <= dfu_proc->dev_count; ++i) {
                        if ((src & (1 << i))) {
                            dfu_proc->ready[i - 1] = true;
                        }
                        else if (dfu_proc->ready[i - 1] == false) {
                            pass = false;
                        }
                    }
                    if (pass == true) {
                        aw_print("====bin index = %d download success", dfu_proc->bin_index);
                        --dfu_proc->bin_index;
                        dfu_proc->downing = false;
                        if (dfu_proc->fp) {
                            fclose(dfu_proc->fp);
                            dfu_proc->fp = NULL;
                        }
                    }
                }
                else {
                    aw_print("bin download error");
                    dfu_proc->error = true;
                    if (dfu_proc->fp) {
                        fclose(dfu_proc->fp);
                        dfu_proc->fp = NULL;
                    }
                }
            }
        }
        else {
            if (ctrl == AWI_DFU_PRO_UPGRADE_BIN_REP_CODE) {
                uint32_t *pv = (uint32_t*)payload;
                if (bytes == 4 && awi_htonl(pv[0]) == dfu_proc->cur_slice_index + 1) {
                    bool pass = true;
                    for (int i = 1; i <= dfu_proc->dev_count; ++i) {
                        if ((src & (1 << i))) {
                            dfu_proc->ready[i - 1] = true;
                        }
                        else if (dfu_proc->ready[i - 1] == false) {
                            pass = false;
                        }
                    }
                    if (pass == true) {
                        uint8_t buffer[BIN_STEP + 4];
                        ++dfu_proc->cur_slice_index;

                        uint32_t *pindex = (uint32_t*)buffer;
                        pindex[0] = awi_htonl(dfu_proc->cur_slice_index);
                        int readed = fread(buffer + 4, 1, BIN_STEP, dfu_proc->fp);
                        m_current_upgrade_size += readed;
                        aw_print("index = %d readed length = %d", dfu_proc->cur_slice_index, readed);
                        if (readed <= 0) {
                            aw_print("readed fail");
                            dfu_proc->error = true;
                            return ;
                        }
                        if (readed != BIN_STEP && dfu_proc->cur_slice_index != dfu_proc->slices_total) {
                            aw_print("slice index [%d / %d] length = %d error", dfu_proc->cur_slice_index, dfu_proc->slices_total, readed);
                            dfu_proc->error = true;
                            return ;
                        }
                        for (int i = 0; i < dfu_proc->dev_count; ++i) {
                            dfu_proc->ready[i] = false;
                        }
                        awi_dfu_send(dfu_proc->dfu, AWI_DFU_PRO_UPGRADE_BIN_REQ_CODE, dfu_proc->dst, 0x01, buffer, readed + 4);
                    }
                }
                else {
                    aw_print("bin download error");
                    dfu_proc->error = true;
                    if (dfu_proc->fp) {
                        fclose(dfu_proc->fp);
                        dfu_proc->fp = NULL;
                    }
                }
            }
        }
    }else{
        /*upgrade stop rep recv*/
        if (ctrl == AWI_DFU_PRO_UPGRADE_STOP_REP_CODE) {
            bool pass = true;
            for (int i = 1; i <= dfu_proc->dev_count; ++i) {
                if ((src & (1 << i))) {
                    dfu_proc->ready[i - 1] = true;
                }
                else if (dfu_proc->ready[i - 1] == false) {
                    pass = false;
                }
            }
            if (pass == true) {
                dfu_proc->success = true;
                dfu_proc->downing = false;
                aw_print("upgrade success");
            }
        }
    }
}

void DeviceUpgradeUtil::dfuVideoProcProcess(){
    bool running  = true;
    DeviceUpgradeNameSpace::dfu_proc_t *dfu_proc = &m_upgrade_dfu;
    while(running){
        aw_print("dfu_proc->error:%d  dfu_proc->downing:%d  dfu_proc->success: %d\n", dfu_proc->error, dfu_proc->downing, dfu_proc->success);
        if (dfu_proc->error == true) {
            aw_print("error and exit");
            break;
        }
        else if (dfu_proc->success == true) {
            aw_print("successful");
            break;
        }
        else if (dfu_proc->downing == false) {
            for (int i = 0; i < dfu_proc->dev_count; ++i) {
                dfu_proc->ready[i] = false;
            }
            dfu_proc->downing = true;
            dfu_proc->dst = 0;
            for (int i = 1; i <= dfu_proc->dev_count; ++i) {
                dfu_proc->dst |= (1 << i);
            }
        

            dfu_proc->flash_block = 0xFBCA0001;
            dfu_proc->bin_size = 0;
            bool res = file_length(dfu_proc->video_file, &dfu_proc->bin_size);
            if (res == false || dfu_proc->bin_size == 0) {
                aw_print("file [%s] length get fail", dfu_proc->video_file);
                dfu_proc->error = true;
                return ;
            }
            m_total_upgrade_size += dfu_proc->bin_size;

            dfu_proc->slices_total = dfu_proc->bin_size / dfu_proc->slice_size;
            if (dfu_proc->bin_size % dfu_proc->slice_size != 0) {
                dfu_proc->slices_total += 1;
            }
            dfu_proc->cur_slice_index = 0;

            uint8_t md5[16];
            res = file_md5(dfu_proc->video_file, md5);
            if (res == false) {
                aw_print("get file [%s] md5 fail", dfu_proc->video_file);
                dfu_proc->error = true;
                return ;
            }

            uint8_t up_buffer[sizeof(struct _dfu_bininfo)];
            struct _dfu_bininfo *up_info = (struct _dfu_bininfo*)up_buffer;
            up_info->flash_block = awi_htonl(dfu_proc->flash_block);
            up_info->slice_size = awi_htonl(dfu_proc->slice_size);
            up_info->slices_total = awi_htonl(dfu_proc->slices_total);
            up_info->bin_size = awi_htonl(dfu_proc->bin_size);
            memcpy(up_info->bin_md5, md5, 16);


            aw_print("slice size = %d index = %d total = %d bin size = %d", dfu_proc->slice_size, dfu_proc->cur_slice_index, dfu_proc->slices_total, dfu_proc->bin_size);


            for (int i = 0; i < dfu_proc->dev_count; ++i) {
                dfu_proc->ready[i] = false;
            }
            awi_dfu_send(dfu_proc->dfu, AWI_DFU_PRO_UPGRADE_VIDEO_BIN_REQ_CODE, dfu_proc->dst, 0x01, up_buffer, sizeof(struct _dfu_bininfo));
            dfu_proc->downing = true;
        }else{
            uint8_t buffer[BUFFER_SIZE];
            int len = read(m_upgrade_dfu.deice_fd, buffer, m_upgrade_dfu.step);

            //aw_print("read len = %d", len);
            if (len != m_upgrade_dfu.step) {
                aw_print("hid read timeout");
                m_upgrade_dfu.error = true;
            }
            else {
            
                printf("recv[");
                for (int i = 0; i < len; ++i) {
                    printf("%x ", buffer[i]);
                }
                printf("]\r\n");
                
                awi_dfu_recv(m_upgrade_dfu.dfu, buffer + 1, len-1);
            }
        }
    }
}



void DeviceUpgradeUtil::deviceVideoUpgradeRecv(uint16_t ctrl, uint16_t dst, uint16_t src, uint16_t version, const uint8_t *payload, int bytes){
    DeviceUpgradeNameSpace::dfu_proc_t* dfu_proc = &m_upgrade_dfu;
    if (ctrl == AWI_DFU_PRO_UPGRADE_VIDEO_BIN_REP_CODE) {
        aw_print("slice size = %d index = %d total = %d bin size = %d", dfu_proc->slice_size, dfu_proc->cur_slice_index, dfu_proc->slices_total, dfu_proc->bin_size);
        uint32_t *pv = (uint32_t*)payload;
        if (bytes == 4 && awi_ntohl(pv[0]) == 0) {
            bool pass = true;
            for (int i = 1; i <= dfu_proc->dev_count; ++i) {
                aw_print("%d : ready = %d", i, dfu_proc->ready[i - 1]);
                if ((src & (1 << i))) {
                    dfu_proc->ready[i - 1] = true;
                }
                else if (dfu_proc->ready[i - 1] == false) {
                    pass = false;
                }
            }
            if (pass == true) {
                for(int j = 0; j < dfu_proc->slices_total; j++){
                    uint8_t buffer[dfu_proc->slice_size + 4];
                    
                    uint32_t *pindex = (uint32_t*)buffer;
                    pindex[0] = awi_htonl(dfu_proc->cur_slice_index);
                    
                    int readed = fread(buffer + 4, 1, dfu_proc->slice_size, dfu_proc->fp);
                    m_current_upgrade_size += readed;
                    if (readed <= 0) {
                        aw_print("readed fail");
                        dfu_proc->error = true;
                        return ;
                    }
                    aw_print("slice index [%d / %d] length = %d error", dfu_proc->cur_slice_index, dfu_proc->slices_total, readed);
                    if (readed != dfu_proc->slice_size && dfu_proc->cur_slice_index != dfu_proc->slices_total - 1) {
                        aw_print("slice index [%d / %d] length = %d error", dfu_proc->cur_slice_index, dfu_proc->slices_total, readed);
                        dfu_proc->error = true;
                        return ;
                    }
                    for (int i = 0; i < dfu_proc->dev_count; ++i) {
                        dfu_proc->ready[i] = false;
                    }
                    
                    awi_dfu_send(dfu_proc->dfu, AWI_DFU_PRO_UPGRADE_VIDEO_BIN_SLICE_REQ_CODE, dfu_proc->dst, 0x01, buffer, readed + 4);
                    ++dfu_proc->cur_slice_index;
                }
            }
        }
        else {
            aw_print("error");
            dfu_proc->error = true;
            if (dfu_proc->fp) {
                fclose(dfu_proc->fp);
                dfu_proc->fp = NULL;
            }
        }
    }else if(ctrl == AWI_DFU_PRO_UPGRADE_VIDEO_BIN_SLICE_REP_CODE){
        uint32_t *pv = (uint32_t*)payload;
        if(pv[0] == 0xFFFFFFFF){
            warnf("upgrade succeed\n");
        }else{
            warnf("upgrade faild\n");
        }
        dfu_proc->success = true;
    }
}
